package com.justgame.project.generator.utils;

import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.justgame.project.cloud.base.entity.StringEncrypt;
import com.justgame.project.generator.container.EntityContainer;
import com.justgame.project.generator.enums.Rule;
import com.justgame.project.generator.schema.Entity;
import com.justgame.project.generator.schema.Field;
import com.justgame.project.generator.schema.Join;
import com.justgame.project.generator.schema.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 模板内调用方法
 * 注意：
 * ~~~  代码中显示灰色未引用属于正常 ~~~
 * ~~~  代码中显示灰色未引用属于正常 ~~~
 * ~~~  代码中显示灰色未引用属于正常 ~~~
 * ~~~  相关代码不要删除 ~~~
 */
public class FreemarkerInnerUtils {
    public Set<String> getImport(List<Service.ServiceEach> serviceEaches){
        Set<String> importSet = new HashSet<>();
        for (Service.ServiceEach serviceEach : serviceEaches) {
            switch (serviceEach.getResultType()){
                case QUERY_LIST:
                    importSet.add("java.util.List;");
                    break;
                case QUERY_ONE:
                    importSet.add("java.util.Optional;");
                    break;
                case UPDATE:
                    importSet.add("org.springframework.util.Assert;");
                    importSet.add("org.springframework.lang.NonNull;");
                    break;
            }

            for (Service.By item : serviceEach.getItems()) {
                if (!item.getRules().isEmpty()){
                    importSet.add("org.springframework.util.Assert;");
                }
                if (item.getField().isEncrypt()){
                    importSet.add(StringEncrypt.class.getName() + ";");
                    continue;
                }
                switch (item.getField().getType()) {
                    case CLOB:
                    case STRING:
                        importSet.add("java.lang.String;");
                        break;
                    case INTEGER:
                        importSet.add("java.lang.Integer;");
                        break;
                    case LONG:
                    case BIG_INTEGER:
                        importSet.add("java.lang.Long;");
                        break;
                    case BOOLEAN:
                        importSet.add("java.lang.Boolean;");
                        break;
                    case BIG_DECIMAL:
                        importSet.add("java.math.BigDecimal;");
                        break;
                    case LOCAL_DATE_TIME:
                        importSet.add("java.time.LocalDateTime;");
                        break;
                }
            }
        }
        return importSet;
    }

    public List<Service.By> addServiceByParams(List<Service.By> params){
        return params.stream().filter(by -> Objects.isNull(by.getInitValue())).collect(Collectors.toList());
    }

    public String getDbColumnType(Service.By by){
        try{
            Field field = by.getField();
            if (field.isEnum()){
                return NameUtils.firstWorldUpper(by.getEntity().getName())+"."+NameUtils.firstWorldUpper(field.getName());
            }
            if (field.isEncrypt()){
                return StringEncrypt.class.getSimpleName();
            }
            DbColumnType columnType = field.getType();
            switch (columnType){
                case CLOB:
                    return String.class.getSimpleName();
                default:
                    return field.getType().getType();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    @SuppressWarnings("all")
    public Set<String> assertChecks(Service.ServiceType serviceType, List<Service.By> byList){
        Set<String> assertChecks = new HashSet<>();
        for (Service.By by : byList) {
            boolean isSelect = List.of(Service.ServiceType.QUERY_ONE,Service.ServiceType.QUERY_LIST, Service.ServiceType.COUNT).contains(serviceType);
            String fieldName = by.getFieldName()+(isSelect ? NameUtils.firstWorldUpper(by.getFilterType().getServiceMethodName()) : "");
            for (Rule rule : by.getRules()) {
                switch (rule.getRuleType()){
                    case NOT_NULL:
                        assertChecks.add(String.format("Assert.notNull(%s,\"必须提供%s\");",fieldName,by.getField().getComment()));
                        break;
                    case STR_LENGTH_MAX:
                        assertChecks.add(String.format("Assert.isTrue(%s == null || %s.length() <= %d,\"%s长度必须小于等于%d\");",fieldName,fieldName,rule.getValue(),fieldName,rule.getValue()));
                        break;
                    case STR_LENGTH_MIN:
                        assertChecks.add(String.format("Assert.isTrue(%s == null || %s.length() >= %d,\"%s长度必须大于等于%d\");",fieldName,fieldName,rule.getValue(),fieldName,rule.getValue()));
                        break;
                    case NUMBER_MAX:
                        assertChecks.add(String.format("Assert.isTrue(%s == null || BigDecimal.valueOf(%s).compareTo(BigDecimal.valueOf(%s)) <= 0,\"%s必须小于等于%s\");",fieldName,fieldName,rule.getValue(),fieldName,rule.getValue()));
                        break;
                    case NUMBER_MIN:
                        assertChecks.add(String.format("Assert.isTrue(%s == null || BigDecimal.valueOf(%s).compareTo(BigDecimal.valueOf(%s)) >= 0,\"%s必须大于等于%s\");",fieldName,fieldName,rule.getValue(),fieldName,rule.getValue()));
                        break;
                    case PHONE:
                        assertChecks.add(String.format("Assert.isTrue(%s != null && %s.matches(\"^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\\\d{8}$\"),\"%s格式不正确\");",fieldName,fieldName,by.getField().getComment()));
                        break;
                    case EMAIL:
                        assertChecks.add(String.format("Assert.isTrue(%s != null && %s.matches(\"^[a-zA-Z0-9_+&*-]+(?:\\\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\\\.)+[a-zA-Z]{2,7}$\"),\"%s格式不正确\");",fieldName,fieldName,by.getField().getComment()));
                        break;
                    case HTTP_URL:
                        assertChecks.add(String.format("Assert.isTrue(%s != null && %s.matches(\"^(https?:\\\\/\\\\/)?(www\\\\.)?([a-zA-Z0-9]+(-?[a-zA-Z0-9])*\\\\.)+[a-zA-Z]{2,}(\\\\/\\\\S*)?$\"),\"%s格式不正确\");",fieldName,fieldName,by.getField().getComment()));
                        break;
                    case IP:
                        assertChecks.add(String.format("Assert.isTrue(%s != null && %s.matches(\"^(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}$\"),\"%s格式不正确\");",fieldName,fieldName,by.getField().getComment()));
                        break;
                }
            }
        }
        return assertChecks;
    }

    public Set<String> getQueryTemplateImports(Entity entity){
        Set<String> imports = new HashSet<>();
        for (Join joinTable : entity.getJoinTables()) {
            imports.add("import com.justgame.project.service."+joinTable.getTargetEntity().getModuleName()+".entity."+joinTable.getTargetEntity().getName()+";");
            imports.add("import com.justgame.project.service."+joinTable.getTargetEntity().getModuleName()+".query."+joinTable.getTargetEntity().getName()+"Query;");
        }
        return imports;
    }

    public String getOtherValue(Service.By by){
        return Objects.isNull(by.getDefaultValue()) ? "null" : by.getDefaultValue();
    }

    public String getClassSimpleName(Field field){
        return field.getType().getType();
    }

    public String getClassSimpleName(Class<?> clazz){
        return clazz.getSimpleName();
    }

    public String getClassPkgName(Class<?> clazz){
        return clazz.getName();
    }

    public Set<String> getEntityTemplateExtraImports(TableInfo tableInfo){
        Optional<Entity> entityOpt = AppContextUtils.getBean(EntityContainer.class).findEntityByTableName(tableInfo.getName());
        Set<String> imports = new HashSet<>();
        if (entityOpt.isEmpty()){
            return imports;
        }
        boolean hasEncrypt = entityOpt.get().getFields().stream().anyMatch(Field::isEncrypt);
        if (hasEncrypt){
            imports.add(StringEncrypt.class.getName());
        }
        return imports;
    }
}
